﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

namespace Neo4jRestSharp.Neo4jRestServices
{
    public class NodeRestServices
    {
        private readonly Uri baseUri;
        private readonly RequestMaker requestMaker;

        public NodeRestServices(Uri baseUri)
        {
            this.baseUri = baseUri;
            requestMaker = new RequestMaker();
        }

        #region Nodes and Node Properties
        /// <summary>
        /// Get root node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)       
        /// </remarks>
        public string GetRootNode()
        {
            HttpWebRequest request = Neo4jRequest.CreateJSONWebRequest(HttpWebRequestMethod.GET, baseUri);
            var response = requestMaker.MakeRequest(request);
            if (response.Item1 != HttpStatusCode.OK)
            {
                throw new Neo4jRestException("Error getting root node for Neo4j", response.Item3);
            }
            return response.Item3;
        }

		/// <summary>
		/// Create node
		/// </summary>
		/// <remarks>
		/// Http Success Response: 201 (node was created)
		/// Http Error Response: 400 (invalid data sent)        
		/// </remarks>
		public string CreateNode()
		{
			return this.CreateNode(null);
		}

        /// <summary>
        /// Create node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 201 (node was created)
        /// Http Error Response: 400 (invalid data sent)        
        /// </remarks>
        public string CreateNode(string nodeData)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node");

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.POST, uri);

			if (!string.IsNullOrWhiteSpace(nodeData))
			{
				HttpHelper.AddBody(request, nodeData);
			}

        	var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.Created)
            {
                throw new Neo4jRestException("Error creating Neo4j Node", response.Item3);
            }

            return response.Item3;
        }

        /// <summary>
        /// Get node by id
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public string GetNode(int nodeId)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId);

            HttpWebRequest request = Neo4jRequest.CreateJSONWebRequest(HttpWebRequestMethod.GET, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.OK)
            {
                throw new Neo4jRestException("Error creating Neo4j Node", response.Item3);
            }

            return response.Item3;
        }

        /// <summary>
        /// Set properties on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void SetNodeProperties(int nodeId, string nodeData)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties");

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.PUT, uri);

            HttpHelper.AddBody(request, nodeData);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.NoContent)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }
        }

        /// <summary>
        /// Get properties on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Success Response: 204 (no properties found)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public string GetNodeProperties(int nodeId)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties");

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.GET, uri);            

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.OK && response.Item1 != HttpStatusCode.NoContent)
            {
				throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);    
            }
			return response.Item3;                
        }

        /// <summary>
        /// Remove all properties on node
        /// </summary>
        /// <remarks>        
        /// Http Success Response: 204 (no properties found)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void RemoveNodeProperties(int nodeId)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties");

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.DELETE, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.NoContent)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }            
        }

        /// <summary>
        /// Set property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void SetNodeProperty(int nodeId, string propertyName, string propertyValue)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties", propertyName);

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.PUT, uri);

            HttpHelper.AddBody(request, propertyValue);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.NoContent)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }      
        }

        /// <summary>
        /// Get property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)        
        /// Http Error Response: 404 (node or property not found)
        /// </remarks>
        public string GetNodeProperty(int nodeId, string propertyName)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties", propertyName);

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.GET, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.OK)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }

            return response.Item3.Replace("\"", "");
        }

        /// <summary>
        /// Remove property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 404 (node or property not found)
        /// </remarks>
        public void RemoveNodeProperty(int nodeId, string propertyName)
        {
            var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "properties", propertyName);

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.DELETE, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.NoContent)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }
        }

        /// <summary>
        /// Delete node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)
        /// Http Error Response: 404 (node not found)
        /// Http Error Response: 409 (Node could not be deleted (still has relationships?))
        /// </remarks>
		public void DeleteNode(int nodeId)
		{
			var uri = HttpHelper.ConcatUri(baseUri, "node", nodeId);

			HttpWebRequest request = Neo4jRequest.CreateJSONWebRequest(HttpWebRequestMethod.DELETE, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.NoContent)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }
        }
        #endregion Nodes and Node Properties

        #region Node Relationships
        /// <summary>
        /// Create node relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 201 (ok, relationship was created)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 405 ("to" node, or the node specified by the URI not found)
        /// </remarks>
        public string CreateRelationship(int nodeId, string relationshipData)
        {           
            Uri uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "relationships");

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.POST, uri);

            HttpHelper.AddBody(request, relationshipData);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.Created)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }

        	return response.Item3;
        }

        /// <summary>
        /// Set properties on relationship
        /// Replaces all properties on a relationship with the supplied set of properties.
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
		public void SetRelationshipProperties(int relationshipId, string relationshipData)
        {
			Uri uri = HttpHelper.ConcatUri(baseUri, "relationship", relationshipId, "properties");

			HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.PUT, uri);

			HttpHelper.AddBody(request, relationshipData);

			var response = requestMaker.MakeRequest(request);

			if (response.Item1 != HttpStatusCode.NoContent)
			{
				throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
			}
		}

        /// <summary>
        /// Get properties on relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Success Response: 204 (no relationships found)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public string GetRelationshipProperties(int relationshipId)
        {
			Uri uri = HttpHelper.ConcatUri(baseUri, "relationship", relationshipId, "properties");

			HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.GET, uri);

			var response = requestMaker.MakeRequest(request);

			if (response.Item1 != HttpStatusCode.OK && response.Item1 != HttpStatusCode.NoContent)
			{
				throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
			}

        	return response.Item3;
        }

        /// <summary>
        /// Remove all properties on relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
		public void RemoveRelationshipProperties(int relationshipId)
        {
			Uri uri = HttpHelper.ConcatUri(baseUri, "relationship", relationshipId, "properties");

			HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.DELETE, uri);

			var response = requestMaker.MakeRequest(request);

			if (response.Item1 != HttpStatusCode.NoContent)
			{
				throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
			}
        }

        /// <summary>
        /// Set a single property on relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void SetRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Get a single property relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void GetRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Remove a single property on relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 404 (relationship or property not found)
        /// </remarks>
        public void RemoveRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Delete relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (no content returned)        
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void RemoveRelationship(int relationshipId)
        {
			var uri = HttpHelper.ConcatUri(baseUri, "relationship", relationshipId);

			HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.DELETE, uri);

			var response = requestMaker.MakeRequest(request);

			if (response.Item1 != HttpStatusCode.NoContent)
			{
				throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
			}
        }

        /// <summary>
        /// Get relationships on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok, no content returned)        
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public string GetRelationshipsOnNode(int nodeId, RelationshipDirection relationshipDirection, IEnumerable<string> relationshipTypes)
        {
        	string types = "";
			
			if (relationshipTypes.Count() > 0)
				types = relationshipTypes.Aggregate((rt1, rt2) => rt1 + "&" + rt2);

            Uri uri = HttpHelper.ConcatUri(baseUri, "node", nodeId, "relationships", relationshipDirection.ToString().ToLower(), types);

            HttpWebRequest request = Neo4jRequest.CreateWebRequest(HttpWebRequestMethod.GET, uri);

            var response = requestMaker.MakeRequest(request);

            if (response.Item1 != HttpStatusCode.OK)
            {
                throw new Neo4jRestException("Error creating properties on Neo4j Node", response.Item3);
            }

            return response.Item3;
        }
        #endregion Node Relationships

        #region Neo4j Indexes
        #endregion Neo4j Indexes

        #region Neo4j Graph Traverses
        #endregion Neo4j Graph Traverses
    }
}